/*
This package is for Build the Docker Images and push them to the local docker registry. Focus the func. DockerBuild()
*/
package cdelivery

import (
	"fmt"
	"hxy/repo/cmd"
	"hxy/repo/config"
	"hxy/repo/database"
	"hxy/repo/microservice"
	"hxy/repo/model"
	"log"
	"os"
	"path"
	"strings"

	docker "github.com/fsouza/go-dockerclient"
)

var client *docker.Client

func init() {
	var err error
	client, err = docker.NewClient(config.DockerLocalClient)
	if err != nil {
		log.Fatal(err)
	}
}
func updateStatus(builder model.DockerImageBuilder, status model.DockerStatus) {
	builder.Status = status
	database.DockerTaskUpdateStatus(builder)
}

// list this fucn
//  1. Get the Git model from the Database
//  2. Create the git log file
//     3.
func dockerBuildSingelService(task *cmd.PipelineTask) {
	log.Printf("CI finish to trigger the Docker Building:git id %d,Commit Hash %s", task.Git_id, task.GitHash)
	gitmodel, re := database.GitById(task.Git_id)
	if re.Error != nil {
		log.Print(
			fmt.Printf("ERROR : task' git id in invalid %d", task.Git_id),
		)
		return
	}
	builder := model.DockerImageBuilder{}
	builder.GitHash = task.GitHash
	builder.Git_id = int(gitmodel.ID)
	builder.Status = model.DockerBuilding
	database.DockerTaskCreate(&builder)

	/// Create the log file in the /logpath/git/GIT_HASH.log
	_log, err := os.Create(config.DockerBuildLogPath + task.GitHash + ".log")
	if err != nil {
		log.Print(err)
		updateStatus(builder, model.DockerError)
		return
	}

	// for git registry's remote repo's name and tag (hash value)
	temp_git_hash := task.GitHash[0:8]
	repo_name := config.DockerRegistryAddr + "/" + gitmodel.Name
	/// Step.1 : Docker Build
	opts := docker.BuildImageOptions{
		Name:         gitmodel.Name,
		OutputStream: _log,
		ContextDir:   task.Dir,
	}
	if err := client.BuildImage(opts); err != nil {
		log.Print(err)
		updateStatus(builder, model.DockerError)
		return
	}

	/// Step.2 : Tag the Image and push to the Docker Registry
	/// firte tag the new image "githash",then just push
	updateStatus(builder, model.DockerPUSH)
	err = client.TagImage(gitmodel.Name, docker.TagImageOptions{
		Repo: repo_name,
		Tag:  temp_git_hash,
	})
	if err != nil {
		log.Print(err)
		updateStatus(builder, model.DockerError)
		return
	}
	push_ops := docker.PushImageOptions{
		Name:         repo_name,
		Tag:          temp_git_hash,
		Registry:     config.DockerRegistryAddr,
		OutputStream: _log,
	}
	if err = client.PushImage(push_ops, docker.AuthConfiguration{
		ServerAddress: config.DockerRegistryAddr,
	}); err != nil {
		updateStatus(builder, model.DockerError)
		return
	}
	/// Step.3 : Tag the current image to the latest
	err = client.TagImage(gitmodel.Name, docker.TagImageOptions{
		Repo: repo_name,
	})
	if err != nil {
		updateStatus(builder, model.DockerError)
		log.Print(err)
		return
	}
	push_ops = docker.PushImageOptions{
		Name:         repo_name,
		Registry:     config.DockerRegistryAddr,
		OutputStream: _log,
	}
	if err = client.PushImage(push_ops, docker.AuthConfiguration{
		ServerAddress: config.DockerRegistryAddr,
	}); err != nil {
		updateStatus(builder, model.DockerError)
		log.Fatal(err)
		return
	}

	updateStatus(builder, model.DockerFinished)
	log.Printf("Docker Build Finish:git id %d,Commit Hash %s", task.Git_id, task.GitHash)
}
func DockerImageBuildeProcess(dockername string, _log *os.File, dir string) error {
	opts := docker.BuildImageOptions{
		Name:         dockername,
		OutputStream: _log,
		ContextDir:   dir,
	}
	if err := client.BuildImage(opts); err != nil {
		return err
	}
	return nil
}
func dockerImageUpdateProcess(dockername string, _log *os.File, hash string) error {
	remote_tag := config.DockerRegistryAddr + "/" + dockername
	err := client.TagImage(dockername, docker.TagImageOptions{
		Repo: remote_tag,
		Tag:  hash,
	})
	if err != nil {
		return err
	}
	push_ops := docker.PushImageOptions{
		Name:         remote_tag,
		Tag:          hash,
		Registry:     config.DockerRegistryAddr,
		OutputStream: _log,
	}
	if err = client.PushImage(push_ops, docker.AuthConfiguration{
		ServerAddress: config.DockerRegistryAddr,
	}); err != nil {
		return err
	}
	/// Step.3 : Tag the current image to the latest
	err = client.TagImage(dockername, docker.TagImageOptions{
		Repo: remote_tag,
	})
	if err != nil {
		return err
	}
	push_ops = docker.PushImageOptions{
		Name:         remote_tag,
		Registry:     config.DockerRegistryAddr,
		OutputStream: _log,
	}
	if err = client.PushImage(push_ops, docker.AuthConfiguration{
		ServerAddress: config.DockerRegistryAddr,
	}); err != nil {
		return err
	}
	return nil
}

func DockerBuild(task cmd.PipelineTask) error {
	gitmodel, re := database.GitById(task.Git_id)
	if re.Error != nil {
		log.Print(
			fmt.Printf("ERROR : task' git id in invalid %d", task.Git_id),
		)
		return re.Error
	}
	//暂时用来判断是不是单体结构
	_, err := microservice.PathofMavenProject(task.Dir)
	if err != nil {
		if strings.Contains(err.Error(), "is not a micro service project") {
			dockerBuildSingelService(&task)
		} else {
			return err
		}
	}
	service_paths := microservice.DockerBuildPathofSubModule(gitmodel)

	// 此处只是用前8位的hash value
	temp_git_hash := task.GitHash[0:8]
	//log path : /log-path-in-config/hash(8)/{service-name}.log
	LogPath_ := path.Join(config.ComplierLogPath, temp_git_hash)
	err = os.MkdirAll(LogPath_, 0750)
	if err != nil {
		return err
	}
	for _, _s_path := range service_paths {
		go func(_s_path string) {
			module_path := path.Join(task.Dir, _s_path)
			builder := model.DockerImageBuilder{}
			builder.GitHash = task.GitHash
			builder.Git_id = int(gitmodel.ID)
			builder.Status = model.DockerBuilding
			builder.ProjectName = _s_path
			database.DockerTaskCreate(&builder)
			_log, err := os.Create(path.Join(LogPath_, builder.ProjectName+".log"))
			if err != nil {
				log.Printf("1DOCKER building err:%s", err)
				updateStatus(builder, model.DockerError)
			}
			err = DockerImageBuildeProcess(gitmodel.Name+"/"+_s_path, _log, module_path)
			if err != nil {
				log.Printf("2DOCKER building err:%s", err)
				updateStatus(builder, model.DockerError)
			}
			err = dockerImageUpdateProcess(gitmodel.Name+"/"+_s_path, _log, temp_git_hash)
			if err != nil {
				log.Printf("3DOCKER building err:%s", err)
				updateStatus(builder, model.DockerError)
			}
			log.Printf("Docker Build Finish:git id %d,Commit Hash %s , module:%s\n", task.Git_id, task.GitHash, _s_path)
			updateStatus(builder, model.DockerFinished)
		}(_s_path)

	}
	return nil
}
